<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework\orm\annotations\relations;

#use \ezcPersistentObjectDefinition;
#use tehframework\Inflector;
#use tehframework\orm\annotations\RelationAnnotation;

class ManyToManyAnnotation extends RelationAnnotation
{
	/**
	 * @var string?
	 */
	protected $relationTable;

	/**
	 * @param  string $relationTable
	 * @return ManyToManyAnnotation
	 */
	public function setRelationTable($relationTable)
	{
		$this->relationTable = empty($relationTable) ? null : $relationTable;
		
		return $this;
	}
	
	/**
	 * @param  ezcPersistentObjectDefinition $def
	 * @param  array<string,ezcPersistentObjectDefinition> $defs
	 * @return ezcPersistentManyToManyRelation
	 * @throws InvalidArgumentException
	 */
	protected function createRelation(
		ezcPersistentObjectDefinition $def, array $defs)
	{
		$rel = new ezcPersistentManyToManyRelation(
			$this->createSourceTable($def),
			$this->createDestinationTable($defs),
			$this->createRelationTable($def, $defs)
		);
		
		$rel->columnMap = $this->createColumnMap($def, $defs);
		$rel->reverse   = $this->reverse;
		
		return $rel;
	}
	
	/**
	 * @param  ezcPersistentObjectDefinition $def
	 * @param  array<string,ezcPersistentObjectDefinition> $defs
	 * @return string
	 * @throws InvalidArgumentException
	 */
	protected function createRelationTable(
		ezcPersistentObjectDefinition $def, array $defs)
	{
		if (!empty($this->relationTable))
		{
			return $this->relationTable;
		}
		
		if (empty($this->entity))
		{
			throw new InvalidArgumentException(
				'Relation table can not be empty, if entity was not specified.'
			);
		}
		
		return $def->table . '_' . $defs[$this->entity]->table;
	}
	
	/**
	 * @param  ezcPersistentObjectDefinition $def
	 * @param  array<string,ezcPersistentObjectDefinition> $defs
	 * @return ezcPersistentDoubleTableMap[]
	 * @throws InvalidArgumentException
	 */
	protected function createColumnMap(
		ezcPersistentObjectDefinition $def, array $defs)
	{
		if ($this->entity !== null)
		{
			return array(new ezcPersistentDoubleTableMap(
				$def->idProperty->columnName,
				$this->createDestinationColumn($def),
				$this->createDestinationColumn($defs[$this->entity]),
				$defs[$this->entity]->idProperty->columnName
			));
		}
		
		if (isset($this->columnMap[0]))
		{
			if (is_array($this->columnMap[0]))
			{
				return $this->createMultipleColumnMaps();
			}
			
			if (isset($this->columnMap[1])
			&& isset($this->columnMap[2])
			&& isset($this->columnMap[3]))
			{
				return $this->createSingleColumnMap();
			}
		}
		
		throw new InvalidArgumentException(
			'Colum map can not be empty or invalid, if entity is not specified.'
		);
	}
	
	/**
	 * @return ezcPersistentDoubleTableMap[]
	 */
	private function createMultipleColumnMaps()
	{
		$columnMaps = array();
		
		foreach ($this->columnMap as $columnMap)
		{
			if (!isset($columnMap[0])
			|| !isset($columnMap[1])
			|| !isset($columnMap[2])
			|| !isset($columnMap[3]))
			{
				continue;
			}
			
			$columnMap[] = new ezcPersistentDoubleTableMap(
				$columnMap[0], $columnMap[1], $columnMap[2], $columnMap[3]
			);
		}
		
		return $columnMap;
	}
	
	/**
	 * @return ezcPersistentDoubleTableMap
	 */
	private function createSingleColumnMap()
	{
		return array(new ezcPersistentDoubleTableMap(
			$this->columnMap[0],
			$this->columnMap[1],
			$this->columnMap[2],
			$this->columnMap[3]
		));
	}
}